#ifndef _L1_data_cache_h_
#define _L1_data_cache_h_

#include <iostream>
#include <stdio.h>
#include "Set.h"
#include "L2_cache.h"

#define nWays 4

using namespace std;

//This struct is instantiated in an array, one for each set of the cache.
//It holds the bits b(i,j) for each cache set that store the order of the
//cache lines from most to least recently used.

struct LRU_4_way {
    bool bits[6];     
    friend ostream& operator<<(ostream& lhs, const LRU_4_way& rhs)//This overloaded operator now prints the LRU bits.
    {
        lhs << rhs.bits[0] << rhs.bits[1] << rhs.bits[2] << rhs.bits[3] << rhs.bits[4] << rhs.bits[5]; 
        return lhs;
    }
    LRU_4_way(){ //Constructor for the LRU, initializes all LRU bits to 0.
        bits[0]=bits[1]=bits[2]=0;
        bits[3]=bits[4]=bits[5]=0;        
    }
};

int LRU_way(LRU_4_way);

class L2_cache;    // forward declarations

class L1_data_cache {

private:

    Set cache[nSets][nWays];
    LRU_4_way LRU[nSets];

public:

/**  Ports and Connections **/
    
    L2_cache* l2;                   // Connection to the L2_cache
    char *filepath;                 // path to the output file
    long reads, writes;             // Read and Write counters (output)
    long misses, hits;              // Misses and Hits counter (output)
    long invalidates, print_req;    // Invalidates and Print counter (output)

/**  Methods **/
    
    L1_data_cache(){
        reads = writes = misses = hits = invalidates = print_req = 0; 
        filepath = (char*) malloc(256);
        sprintf(filepath, "print_data.txt");
        FILE *fid = fopen(filepath,"w");
        fclose(fid);
    }
    
    unsigned int read(unsigned int addr);
    void write(unsigned int addr);
    void modify(unsigned int addr, unsigned int way, unsigned int dat);
    void invalidate(unsigned int addr);
    void evict(unsigned int addr, unsigned int way);
    int searchWays(unsigned int addr);
    unsigned int findReplacementWay(unsigned int addr);
    void PrintContents();
    void ResetState();

/** Address Parser **/

    static unsigned int getOffSet(unsigned int addr) {        
        return addr & OffSetMask;          
    }        
    
    static unsigned int getIndex(unsigned int addr) {        
        return (addr >> nOffSetBits) & IndexMask;          
    }

    static unsigned int getTag(unsigned int addr) {        
        return (addr >> nOffSetBits >> nIndexBits) & TagMask;          
    }    
};

#endif
